﻿#region Copyright & License

// Copyright © 2012 François Chabot, Yves Dierick
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#endregion

using System;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;

namespace Be.Stateless.Logging.Appender
{
	[TestFixture]
	public class EventLogAppenderFixture
	{
		#region Setup/Teardown

		[SetUp]
		public void SetUp()
		{
			Tag = Guid.NewGuid().ToString();
		}

		#endregion

		[Test]
		[Explicit("You need elevated rights to run this test.")]
		[Category("Elevated")]
		public void ActivatingAppenderOptionsWillChangeEventSourceIfNecessary()
		{
			const string source = "Be.Stateless.Logging.EventSourceFromOtherEventLog";
			try
			{
				if (EventLog.SourceExists(source)) EventLog.DeleteEventSource(source, MACHINE_NAME);

				EventLog.CreateEventSource(new EventSourceCreationData(source, "System"));
				var logName = EventLog.LogNameFromSourceName(source, MACHINE_NAME);
				Assert.AreEqual("System", logName);

				var appender = new EventLogAppender { Source = source };
				appender.ActivateOptions();

				logName = EventLog.LogNameFromSourceName(source, MACHINE_NAME);
				Assert.AreEqual(APPLICATION_LOG_NAME, logName);
			}
			finally
			{
				EventLog.DeleteEventSource(source, MACHINE_NAME);
			}
		}

		[Test]
		[Explicit("You need elevated rights to run this test.")]
		[Category("Elevated")]
		public void ActivatingAppenderOptionsWillCreateEventSourceIfNecessary()
		{
			const string source = "Be.Stateless.Logging.UnexistingEventSource";
			try
			{
				if (EventLog.SourceExists(source)) EventLog.DeleteEventSource(source, MACHINE_NAME);

				var appender = new EventLogAppender { Source = source };
				appender.ActivateOptions();

				var logName = EventLog.LogNameFromSourceName(source, MACHINE_NAME);
				Assert.AreEqual(APPLICATION_LOG_NAME, logName);
			}
			finally
			{
				EventLog.DeleteEventSource(source, MACHINE_NAME);
			}
		}

		[Test]
		public void LogCriticalLevelEvent()
		{
			try
			{
				throw new Exception();
			}
			catch (Exception ex)
			{
				_logger.Critical(Tag, ex);
				AssertEqual(FindEntry(Tag), CategoryLevel.Critical, ex);
			}
		}

		[Test]
		public void LogErrorLevelEvent()
		{
			try
			{
				throw new ArgumentNullException();
			}
			catch (ArgumentNullException ex)
			{
				_logger.Error(Tag, ex);
				AssertEqual(FindEntry(Tag), CategoryLevel.Error, ex);
			}
		}

		[Test]
		public void LogInfoLevelEvent()
		{
			_logger.Info(Tag);
			AssertEqual(FindEntry(Tag), CategoryLevel.Info);
		}

		[Test]
		public void LogWarningLevelEvent()
		{
			_logger.Warn(Tag);
			AssertEqual(FindEntry(Tag), CategoryLevel.Warn);
		}

		[Test]
		[Ignore("Issue with EventLogAppender.MAX_MESSAGE_LENGTH")]
		public void TooLargeLogMessageIsTruncated()
		{
			try
			{
				throw new ArgumentNullException();
			}
			catch (ArgumentNullException ex)
			{
				_logger.Error(Tag.PadRight(EventLogAppender.MAX_MESSAGE_LENGTH, '0'), ex);
				var entry = FindEntry(Tag);
				Assert.IsTrue(entry.Message.Contains("[Truncated Message!]"));
				var data = BitConverter.ToString(entry.Data);
				Assert.IsFalse(data.Contains("[Truncated Message!]"));
				Assert.IsTrue(data.Length > entry.Message.Length);

				AssertEqual(entry, CategoryLevel.Error, ex, true);
			}
		}

		private string Tag { get; set; }

		private static EventLogEntry FindEntry(string tag)
		{
			var eventLog = new EventLog("Application", ".", TEST_SOURCE);
			return eventLog.Entries.Cast<EventLogEntry>().First(e => e.Message.StartsWith(tag));
		}

		private static void AssertEqual(EventLogEntry actualEntry, CategoryLevel expectedCategoryLevel, Exception expectedException = null, bool truncated = false)
		{
			Assert.AreEqual(TEST_SOURCE, actualEntry.Source);

			if (expectedCategoryLevel >= CategoryLevel.Error) Assert.AreEqual(EventLogEntryType.Error, actualEntry.EntryType);
			else if (expectedCategoryLevel == CategoryLevel.Warn) Assert.AreEqual(EventLogEntryType.Warning, actualEntry.EntryType);
			else Assert.AreEqual(EventLogEntryType.Information, actualEntry.EntryType);

			Assert.AreEqual((short) expectedCategoryLevel, actualEntry.CategoryNumber);
			Assert.AreEqual(expectedException == null ? 0 : EventLogAppender.GetEventId(expectedException), actualEntry.InstanceId);
			Assert.IsTrue(truncated ? actualEntry.Data.Length > 0 : actualEntry.Data.Length == 0);
		}

		private const string TEST_SOURCE = "Be.Stateless.Logging.EventSource";
		private const string MACHINE_NAME = ".";
		private const string APPLICATION_LOG_NAME = "Application";

		private static readonly ILog _logger = LogManager.GetLogger(typeof(EventLogAppenderFixture));
	}
}